/*
*  Engine.cpp
*  SypXIPhone
*
*  Created by Luv Khemani on 5/13/10.
*  Copyright 2010 Luv Khemani. All rights reserved.
*
*/

#include <sstream>
#include <ft2build.h>
#include FT_FREETYPE_H

#ifdef SYPX_MEMCHECK
#include "mmgr.h"
#endif

#include "Engine.h"
#include "Base.h"
#include "Canvas.h"
#include "CanvasDesktopGL.h"
#include "ResourceManager.h"
#include "RendererGL2.h"
#include "AudioEngine.h"
#include "AudioEngineOAL.h"
#include "ErrorLogger.h"
#include "Font.h"
#include "Texture.h"

int nextPowerOf2 (int a )
{
	int rval=1;
	// rval<<=1 Is A Prettier Way Of Writing rval*=2;
	while(rval<a) rval<<=1;
	return rval;
}

namespace SypX
{

	Engine::Engine()
	{
		runFunction = 0;

	}

	Engine::~Engine()
	{
		ResourceManager::destroy();
		delete audioEng;
		delete render;
		delete canvas;
	}

	void Engine::init(UInt width, UInt height)
	{
		std::cout<<canvas->getElapsedTime()<<" Begin Init\n";
		ResourceManager::create();
		std::cout<<canvas->getElapsedTime()<<" Create Resource Manager Init\n";
		//Init OpenGLES
		render = SypX::Engine::createRenderer();
		render->init(canvas->getWidth(), canvas->getHeight());	//Init Renderer
		std::cout<<canvas->getElapsedTime()<<" Create & Init Renderer\n";
		audioEng = createAudioEngine();							//Create audio engine
		std::cout<<canvas->getElapsedTime()<<" Create Audio Engine\n";
		//Init Freetype
		int error = FT_Init_FreeType( &ft2Lib );
		assert(!error && "Error initialising FreeType2" );

		std::cout<<canvas->getElapsedTime()<<" Init FreeType\n";


		initFunction();//Call userland init after subsystems have been initialised
		std::cout<<canvas->getElapsedTime()<<" User Init\n";
	}

	void Engine::registerRunLoop(void (*runLoopCallBack)(void))
	{
		runFunction = runLoopCallBack;
	}

	void Engine::registerInitFunction(void (*initCallBack)(void))
	{
		initFunction = initCallBack;

	}

	AudioEngine* Engine::getAudioEngine()
	{
		return audioEng;
	}

	Renderer* Engine::getRenderer()
	{
		return render;
	}

	Canvas* Engine::getCanvas()
	{
		return canvas;
	}

	void Engine::removeFont(Font* f)
	{

		ResourceManager::getSingleton()->removeResource(f);
		delete f;

	}

	Font* Engine::loadFontFromFile(const SypX::String& name, int size, int MARGIN, bool genColor)
	{
		std::ostringstream oss;
		oss<<name<<"_"<<size;
		String fname = oss.str();

		Font* f = static_cast<Font*>(ResourceManager::getSingleton()->checkResource(fname));
		if(f)
		{
			return f;
		}

		String path = Engine::getFilePath(name);
		f = new Font();
		f->setFileName(fname);	//we want to set the filename in records as FONTFILE_SIZE because we want to allow multiple fonts of same face but different size

		int error = FT_New_Face( ft2Lib, path.c_str(), 0, &f->face );

		assert ( error != FT_Err_Unknown_File_Format && "the font file could be opened and read, but it appears that its font format is unsupported");
		assert ( !error && "Invalid Font format");

		FT_Set_Char_Size(
			f->face,    /* handle to face object           */
			0,       /* char_width in 1/64th of points  */
			size*64,   /* char_height in 1/64th of points */
			72,     /* horizontal device resolution    */
			72 );   /* vertical device resolution      */

		int glyph_index = FT_Get_Char_Index( f->face, 87 );
		//We render 1 glyph to see how big it is, in this case W (87)
		FT_Load_Glyph(//load 1 character
			f->face,          /* handle to face object */
			glyph_index,   /* glyph index           */
			FT_LOAD_DEFAULT );

		if( f->face->glyph->format != FT_GLYPH_FORMAT_BITMAP )
		{//if its not in bitmap form, render it
			FT_Render_Glyph( f->face->glyph,   /* glyph slot  */
				FT_RENDER_MODE_NORMAL  ); /* render mode */
		}

		//Now we create our texture
		UShort texH, texW;
		texW = nextPowerOf2( (f->face->glyph->bitmap.width + MARGIN) * 16 );//16 chars a row
		texH = nextPowerOf2( (f->face->glyph->bitmap.rows + MARGIN) * 8 );//8 chars a column

		f->fontSizeX = texW/16.0f;
		f->fontSizeY = texH/8.0f;

		if(texW > 512)
		{
			ErrorLogger::getLogger()<<"Memory Warning : "<<fname<<" generated font texture larger than 512X512\n";
		}

		f->tex = new Texture();
		f->tex->setFileName(fname + "_texture");
		f->mi = new MeshInstance();
		f->mi->useMesh(f);
		f->mi->useTexture(f->tex);

		f->tex->getDataRef().resize(texH * texW);
		UByteArray& data = f->tex->getDataRef();			//allocate our texture memory

		//Starting points for font texture
		UInt currX = 0;
		UInt currY = texH - (texH/8) - 1;	//We want to draw from top to btm(-1 because we start from 0)

		UInt renderedThisLine = 0;
		//PInt glyphBytes;
		for( UShort i = 32; i < 127 ; ++i )
		{//for every character (ranging from ' ' to '?)
			glyph_index = FT_Get_Char_Index( f->face, i );
			FT_Load_Glyph(//load 1 character
				f->face,          /* handle to face object */
				glyph_index,   /* glyph index           */
				FT_LOAD_DEFAULT );

			if( f->face->glyph->format != FT_GLYPH_FORMAT_BITMAP )
			{//if its not in bitmap form, render it
				FT_Render_Glyph( f->face->glyph,   /* glyph slot  */
					FT_RENDER_MODE_NORMAL  ); /* render mode */
			}
			//For debugging
			//ErrorLogger::getLogger()<<"Rows for "<<static_cast<char>(i)<<" : "<<f->face->glyph->bitmap.rows<<" ADVANCE : "<<(f->face->glyph->metrics.horiAdvance >> 6)<<" TOP : ";
			//ErrorLogger::getLogger()<<f->face->glyph->bitmap_top<<"\n";

			//Done rendering, now copy over the glyph to our texture buffer
			for( int j = 0; j < f->face->glyph->bitmap.rows; ++j )//for every row in this glyph
			{
				for(int k = 0; k < f->face->glyph->bitmap.width; ++k)
				{//For every column
					//Y = Top Left pixel																										//X= currX + K
					data[ ((  currY + f->face->glyph->bitmap_top - j + MARGIN ) * texW) + (currX + k + f->face->glyph->bitmap_left) ] = (f->face->glyph->bitmap.buffer[ j * f->face->glyph->bitmap.width + k ]) ;
				}
			}

			//this->fontWidths[glyph_index] = face->glyph->bitmap.pitch + MARGIN;
			//Note down width of this character to know how much to space out the characters
			f->fontWidths[ i - 32 ] = static_cast<UShort>(f->face->glyph->metrics.horiAdvance >> 6);
			currX += texW/16;		//Move X pointer forward
			++renderedThisLine;
			if( renderedThisLine >= 16 )
			{	//Once we have rendered 16 characters
				//Move down a line
				currY -= texH/8;
				currX = 0;
				renderedThisLine = 0;
			}
		}

		f->tex->setFormats(ALPHAX, ALPHA8);
		f->tex->setWidth(texW);
		f->tex->setHeight(texH);
		f->tex->submitToGPU();
		if(genColor)
		{
			f->setStride(36);
			f->hasColor = true;
		}
		else
		{
			f->setStride(20);
			f->hasColor = false;
		}

		return f;

	}



}
